package com.hzzftech.watchdog.busi.service.impl;

import com.hzzftech.watchdog.busi.constants.BusiConstant;
import com.hzzftech.watchdog.busi.core.executor.DispatcherStatus;
import com.hzzftech.watchdog.busi.core.file.repository.FileRepositoryManager;
import com.hzzftech.watchdog.busi.core.file.repository.KtGitRepositoryFileParser;
import com.hzzftech.watchdog.busi.domain.KtDispatcher;
import com.hzzftech.watchdog.busi.domain.KtRepository;
import com.hzzftech.watchdog.busi.domain.KtRepositoryLog;
import com.hzzftech.watchdog.busi.encrypt.DESEncrypt;
import com.hzzftech.watchdog.busi.mapper.KtRepositoryMapper;
import com.hzzftech.watchdog.busi.service.IKtDispatcherService;
import com.hzzftech.watchdog.busi.service.IKtRepositoryFileService;
import com.hzzftech.watchdog.busi.service.IKtRepositoryLogService;
import com.hzzftech.watchdog.busi.service.IKtRepositoryService;
import com.hzzftech.watchdog.busi.utils.FileUtils;
import com.hzzftech.watchdog.busi.utils.GitOperationUtil;
import com.hzzftech.watchdog.common.core.text.Convert;
import com.hzzftech.watchdog.common.utils.ShiroUtils;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.*;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 文件资源库Service业务层处理
 *
 * @author liquanxiang
 * @date 2021-11-27
 */
@Service("ktRepoService")
public class KtRepositoryServiceImpl implements IKtRepositoryService {
    @Autowired
    private KtRepositoryMapper ktRepositoryMapper;

    @Autowired
    private IKtRepositoryLogService repositoryLogService;

    @Value("${kt-watchdog.git_file_repository_location}")
    private String GIT_FILE_REPOSITORY_LOCATION;

    public static final Logger logger = LoggerFactory.getLogger(KtRepositoryServiceImpl.class);

    public List<KtRepository> listAllEnableRepo() {
        KtRepository repo = new KtRepository();
        repo.setStatus(BusiConstant.STATUS_YES);
        return ktRepositoryMapper.selectKtRepositoryList(repo);
    }

    /**
     * 查询文件资源库
     *
     * @param repositoryId 文件资源库主键
     * @return 文件资源库
     */
    @Override
    public KtRepository selectKtRepositoryByRepositoryId(Long repositoryId) {
        return ktRepositoryMapper.selectKtRepositoryByRepositoryId(repositoryId);
    }

    /**
     * 查询文件资源库列表
     *
     * @param ktRepository 文件资源库
     * @return 文件资源库
     */
    @Override
    public List<KtRepository> selectKtRepositoryList(KtRepository ktRepository) {
        return ktRepositoryMapper.selectKtRepositoryList(ktRepository);
    }

    /**
     * 新增文件资源库
     *
     * @param ktRepository 文件资源库
     * @return 结果
     */
    @Override
    public int insertKtRepository(KtRepository ktRepository) {
        return ktRepositoryMapper.insertKtRepository(ktRepository);
    }

    /**
     * 修改文件资源库
     *
     * @param ktRepository 文件资源库
     * @return 结果
     */
    @Override
    public int updateKtRepository(KtRepository ktRepository) {
        return ktRepositoryMapper.updateKtRepository(ktRepository);
    }

    @Autowired
    private IKtDispatcherService dispatcherService;

    /**
     * 批量删除文件资源库
     *
     * @param repositoryIds 需要删除的文件资源库主键
     * @return 结果
     */
    @Override
    public int deleteKtRepositoryByRepositoryIds(String repositoryIds) {
        if (StringUtils.isEmpty(repositoryIds)) return 0;
        for (String id : Convert.toStrArray(repositoryIds)) {
            Long _id = Long.parseLong(id);
            KtDispatcher ktDispatcher = new KtDispatcher();
            ktDispatcher.setRepositoryId(_id);
            List<KtDispatcher> list = dispatcherService.selectKtDispatcherList(ktDispatcher, BusiConstant.REPOSITORY_TYPE_GIT);

            if (!list.isEmpty()) {
                return -1;
            }

            KtRepository ktRepository = selectKtRepositoryByRepositoryId(_id);

            // 删除文件系统中文件
            FileUtils.delete(GIT_FILE_REPOSITORY_LOCATION + File.separator + ktRepository.getRepositoryLocation());
        }
        return ktRepositoryMapper.deleteKtRepositoryByRepositoryIds(Convert.toStrArray(repositoryIds));
    }

    /**
     * 删除文件资源库信息
     *
     * @param repositoryId 文件资源库主键
     * @return 结果
     */
    @Override
    public int deleteKtRepositoryByRepositoryId(Long repositoryId) {
        return ktRepositoryMapper.deleteKtRepositoryByRepositoryId(repositoryId);
    }

    @Value("${kt-watchdog.git_file_repository_location}")
    private String fileRepositoryLocation;

    @Value("${kt-watchdog.DES_SECURITY_KEY}")
    private String desSecurityKey;


    public KtRepositoryLog commonRepositoryLogHandle(KtRepository ktRepository) {

        KtRepositoryLog repositoryLog = new KtRepositoryLog();
        repositoryLog.setRepositoryId(ktRepository.getRepositoryId());
        repositoryLog.setGitBranch(ktRepository.getGitBranch());
        repositoryLog.setGitRemoteUri(ktRepository.getGitUri());
        repositoryLog.setCreateBy(ShiroUtils.getLoginName());

        return repositoryLog;
    }

    /**
     * 更新GIT仓库
     * @param repositoryId
     * @return
     */
    @Override
    public boolean updateGitResources(Long repositoryId) {
        if (null == repositoryId) {
            return false;
        }

        KtRepository ktRepository = ktRepositoryMapper.selectKtRepositoryByRepositoryId(repositoryId);
        if (Objects.isNull(ktRepository)) {
            return false;
        }

        if (StringUtils.isEmpty(ktRepository.getRepositoryLocation())) {
            return false;
        }
        String fileAbsolutePath = fileRepositoryLocation + File.separator + ktRepository.getRepositoryLocation();
        File repUrlf = new File(fileAbsolutePath);
        Git git = null;
        KtRepositoryLog repositoryLog = commonRepositoryLogHandle(ktRepository);

        if (repUrlf.exists() && new File(fileAbsolutePath, ".git").exists()) {
            repositoryLog.setGitCommand("pull");

            try {
                git = Git.open(repUrlf);
                // 切换分支
                GitOperationUtil.checkOut(git, ktRepository.getGitBranch());
                PullCommand pull = git.pull();
                if (BusiConstant.AUTH_TYPE_SSH.equals(ktRepository.getGitAuthType())) {
                    SshSessionFactory sshSessionFactory = buildSshSessionFactory(ktRepository);
                    pull.setTransportConfigCallback( transport -> ((SshTransport)transport).setSshSessionFactory(sshSessionFactory));
                } else {
                    pull.setCredentialsProvider(new UsernamePasswordCredentialsProvider(ktRepository.getGitUsername(), DESEncrypt.decrypt(ktRepository.getGitPassword(), desSecurityKey)));
                }

                MergeResult mergeResult = pull.setRemoteBranchName(ktRepository.getGitBranch()).call().getMergeResult();
                Iterable<RevCommit> gitLogRes = git.log().add(git.getRepository().resolve(mergeResult.getNewHead().getName())).call();
                RevCommit commit = gitLogRes.iterator().next();
                repositoryLog.setGitCommitedTime(commit.getAuthorIdent().getWhen());
                repositoryLog.setGitCommitedPerson(commit.getAuthorIdent().getName());
                repositoryLog.setGitCommitedMessage(commit.getFullMessage());
                repositoryLog.setGitCommitedHash(mergeResult.getNewHead().getName());
                repositoryLog.setGitRemoteUri(ktRepository.getGitUri());
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_SUCCESS);

                // 更新数据库
                (new FileRepositoryManager(GIT_FILE_REPOSITORY_LOCATION, ktRepository.getRepositoryLocation(), new KtGitRepositoryFileParser(fileService, repositoryId, git))).parse();

            } catch (Exception e) {
                logger.error("执行PULL命令失败", e);
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_FAILED);
                repositoryLog.setGitExecutedErrorMsg(e.getMessage());
                return false;
            } finally {
                if (!Objects.isNull(git)) {
                    git.close();
                }
            }

        } else {
            // clone 操作
            FileUtils.delete(fileAbsolutePath);
            repositoryLog.setGitCommand("clone"); //
            //克隆代码库命令
            CloneCommand cloneCommand = Git.cloneRepository();
            try {
                if (BusiConstant.AUTH_TYPE_SSH.equals(ktRepository.getGitAuthType())) {
                    SshSessionFactory sshSessionFactory = buildSshSessionFactory(ktRepository);
                    cloneCommand.setTransportConfigCallback( transport -> ((SshTransport)transport).setSshSessionFactory(sshSessionFactory));
                } else {
                    cloneCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(ktRepository.getGitUsername(), DESEncrypt.decrypt(ktRepository.getGitPassword(), desSecurityKey)));
                }

                git = cloneCommand
                        .setURI(ktRepository.getGitUri()) //设置远程URI
                        .setBranch(ktRepository.getGitBranch())
                        .setDirectory(repUrlf) //设置下载存放路径
                        .call();
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_SUCCESS);

                // 更新数据库
                (new FileRepositoryManager(GIT_FILE_REPOSITORY_LOCATION, ktRepository.getRepositoryLocation(), new KtGitRepositoryFileParser(fileService, repositoryId, git))).parse();
            } catch (Exception e) {
                logger.error("clone仓库失败", e);
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_FAILED);
                repositoryLog.setGitExecutedErrorMsg(e.getMessage());
                return false;
            } finally {
                if (git != null) {
                    git.close();
                }
            }
        }
        repositoryLogService.addCommonHandle(repositoryLog);
        repositoryLogService.insertKtRepositoryLog(repositoryLog);

        return true;
    }

    @Autowired
    private IKtRepositoryFileService fileService;

    @Override
    public boolean forceUpdateGitResources(Long repositoryId) {
        KtRepository ktRepository = ktRepositoryMapper.selectKtRepositoryByRepositoryId(repositoryId);
        if (Objects.isNull(ktRepository)) {
            return false;
        }
        String fileAbsolutePath = fileRepositoryLocation + File.separator + ktRepository.getRepositoryLocation();
        FileUtils.delete(fileAbsolutePath);

        return updateGitResources(repositoryId);
    }

    @Override
    public List<String> findAllBranch(Long repositoryId) {
        KtRepository ktRepository = ktRepositoryMapper.selectKtRepositoryByRepositoryId(repositoryId);
        return getRemoteBranchs(ktRepository);
    }

    @Override
    public boolean checkRespNameOnly(String repositoryName) {
        if (StringUtils.isEmpty(repositoryName)) {
            throw new IllegalArgumentException("repositoryName can`t be null!");
        }
        return Objects.isNull(ktRepositoryMapper.selectByRepositoryName(repositoryName));
    }

    @Override
    public boolean checkRespLocationOnly(String repositoryLocation) {
        if (StringUtils.isEmpty(repositoryLocation)) {
            throw new IllegalArgumentException("repositoryLocation can`t be null!");
        }
        return Objects.isNull(ktRepositoryMapper.selectByRepositoryLocation(repositoryLocation));
    }

    public List<String> getRemoteBranchs(KtRepository ktRepository) {

        Collection<Ref> refList = null;

         KtRepositoryLog repositoryLog = commonRepositoryLogHandle(ktRepository);
        repositoryLog.setGitCommand("branch");
        if (ktRepository.getGitAuthType().equals(BusiConstant.AUTH_TYPE_PASS)) {
            try {
                String gitUsername = ktRepository.getGitUsername();
                String gitPassword = DESEncrypt.decrypt(ktRepository.getGitPassword(), desSecurityKey);
                if (StringUtils.isNotEmpty(gitUsername) && StringUtils.isNotEmpty(gitPassword)) {
                    UsernamePasswordCredentialsProvider pro = new UsernamePasswordCredentialsProvider(gitUsername, gitPassword);
                    refList = Git.lsRemoteRepository().setRemote(ktRepository.getGitUri()).setCredentialsProvider(pro).call();
                } else {
                    refList = Git.lsRemoteRepository().setRemote(ktRepository.getGitUri()).call();
                }
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_SUCCESS);
            } catch (Exception e) {
                logger.error("获取分支失败", e);
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_FAILED);
                return new ArrayList<>();
            }

        } else if(ktRepository.getGitAuthType().equals(BusiConstant.AUTH_TYPE_SSH)) {

            SshSessionFactory sshSessionFactory = buildSshSessionFactory(ktRepository);
            if (sshSessionFactory == null) {
                return new ArrayList<>();
            }

            try {
                refList = Git.lsRemoteRepository()
                        .setTransportConfigCallback(transport -> {
                            SshTransport sshTransport = (SshTransport) transport;
                            sshTransport.setSshSessionFactory(sshSessionFactory);
                        })
                        .setRemote(ktRepository.getGitUri())
                        .call();
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_SUCCESS);
            } catch (GitAPIException e) {
                logger.error("获取分支失败",e);
                repositoryLog.setGitExecutedStatus(BusiConstant.GIT_COMMAND_FAILED);
                return new ArrayList<>();
            }
        }

        List<String> branchnameList = new ArrayList<>(4);
        for (Ref ref : refList) {
            String refName = ref.getName();
            if (refName.startsWith("refs/heads/")) {                       //须要进行筛选
                String branchName = refName.replace("refs/heads/", "");
                branchnameList.add(branchName);
            }
        }

        repositoryLogService.insertKtRepositoryLog(repositoryLog);

       return branchnameList;
    }

    public SshSessionFactory buildSshSessionFactory(KtRepository ktRepository) {
        SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
            @Override
            protected void configure(OpenSshConfig.Host host, Session session) {
                session.setConfig("StrictHostKeyChecking", "no");
            }

            @Override
            protected JSch createDefaultJSch(FS fs) throws JSchException {
                JSch sch = super.createDefaultJSch(fs);
                String sshKey = null;
                try {
                    sshKey = DESEncrypt.decrypt(ktRepository.getGitSshKey(), desSecurityKey);
                } catch (Exception e) {
                    logger.error("SSH KEY解密失败", e);
                    return null;
                }
                sch.addIdentity("", sshKey.getBytes(StandardCharsets.UTF_8), null, null); //添加私钥文件
                return sch;
            }
        };

        return sshSessionFactory;
    }

    @Override
    public void deleteResource(String repositoryLocation) {
        FileUtils.delete(GIT_FILE_REPOSITORY_LOCATION+"/"+repositoryLocation);
    }
}
